CloudFrontとS3を使ったWebサイトでWebPの画像を配信してみた
WebPに対応している場合はWebPを配信したい
こんにちは、のんピ(@non____97)です。
皆さんはCloudFrontとS3を使ったWebサイトで、クライアントがWebPに対応している場合はWebPを配信したいなと思ったことはありますか? 私はあります。
JPEGやPNGではなく、WebPを使用した方が配信速度の向上が見られるなどメリットを享受することが可能です。
ただし、IEや古いSafari、Firefoxなどごくごく稀にWebPに対応していないことがあります。
そのような場合はWebPではなく、JPEGを配信したいところです。
WebPをサポートしているかはHTTPリクエストヘッダーのAccept
にimage/webp
を含んでいるかで判断できます。2025/1/24時点ではS3単体でリクエストヘッダーをベースにレスポンスするオブジェクトを切り替えることはできません。
ということで、CloudFront Functions、もしくはLambda@EdgeでAccept
ヘッダーをベースに動的にレスポンスする画像ファイルを切り替えてみました。
いきなりまとめ
- 動的にWebPをレスポンスするか判定するためには
Accept
ヘッダーおよび CloudFront Functions or Lambda@Edgeを使おう- CloudFront Functionsの場合は、全ての画像ファイルがWebP形式に対応していることが前提
- 対応していない場合は404が返る
- Lambda@Edgeの場合は、オブジェクトが存在するか判定する時間的余裕があるため、
.webp
が存在しなければクライアントのオリジナルのURIのファイルを返す
- CloudFront Functionsの場合は、全ての画像ファイルがWebP形式に対応していることが前提
やってみた
検証環境
検証環境は以下のとおりです。
※ 本当はAthenaのクエリ出力先S3バケットやGlueテーブルなどもありますが、割愛
検証環境は全てAWS CDKでデプロイしました。使用したコードは以下GitHubリポジトリに保存しています。
こちらのベースとなったコードの詳細な説明は以下記事をご覧ください。
以降、これらの記事で紹介しているコードからの主な変更点を紹介します。
コンテンツのデプロイ
まずはコンテンツのデプロイです。
コンテンツのデプロイはWebPかそれ以外かの2回に分けて行っています。
// Deploy contents
if (!props.contentsPath) {
return;
}
const asset = cdk.aws_s3_deployment.Source.asset(props.contentsPath, {
exclude: [".DS_Store"],
});
new cdk.aws_s3_deployment.BucketDeployment(this, "DeployContents", {
sources: [asset],
destinationBucket: props.websiteBucketConstruct.bucket,
exclude: ["*.webp"],
distribution,
distributionPaths: ["/*"],
});
new cdk.aws_s3_deployment.BucketDeployment(this, "DeployContentsWebP", {
sources: [asset],
destinationBucket: props.websiteBucketConstruct.bucket,
exclude: ["*"],
include: ["*.webp"],
distribution,
distributionPaths: ["/*"],
contentType: "image/webp",
});
というのもaws_s3_deployment.BucketDeploymentで、まとめて全てデプロイすると、WebPのオブジェクトのcontentType
がbinary/octet-stream
となってしまいます。
binary/octet-stream
の場合、ブラウザ上に画面は表示されず、画像ファイルをダウンロードする形になってしまいます。
そのため、対応として拡張子がwebp
のファイルについては明示的にcontentType
でimage/webp
を指定しています。
ちなみに今回WebPファイルの生成にはcwebpを使用しました。
> cwebp non__97.png -o non__97.png.webp
Saving file 'non__97.png.webp'
File: non__97.png
Dimension: 421 x 440
Output: 5464 bytes Y-U-V-All-PSNR 44.77 49.16 50.29 45.88 dB
(0.24 bpp)
block count: intra4: 166 (21.96%)
intra16: 590 (78.04%)
skipped: 560 (74.07%)
bytes used: header: 96 (1.8%)
mode-partition: 905 (16.6%)
Residuals bytes |segment 1|segment 2|segment 3|segment 4| total
macroblocks: | 1%| 7%| 15%| 78%| 756
quantizer: | 36 | 36 | 31 | 24 |
filter level: | 11 | 8 | 5 | 4 |
> ls -lh | grep -e png -e jpeg
-rw-r--r--@ 1 <ユーザー名> <グループ名> 34K 10 11 2016 non__97.jpeg
-rw-r--r--@ 1 <ユーザー名> <グループ名> 75K 7 2 2024 non__97.png
-rw-r--r--@ 1 <ユーザー名> <グループ名> 5.3K 1 24 10:52 non__97.png.webp
-rw-r--r--@ 1 <ユーザー名> <グループ名> 125K 1 24 10:49 non__97_2.png
キャッシュポリシー
Accept
ヘッダーごとにキャッシュするようにキャッシュポリシーを設定しました。
const acceptCachePolicy = new cdk.aws_cloudfront.CachePolicy(
this,
"AcceptCachePolicy",
{
defaultTtl: cdk.Duration.days(1),
minTtl: cdk.Duration.seconds(1),
maxTtl: cdk.Duration.days(7),
cookieBehavior: cdk.aws_cloudfront.CacheCookieBehavior.none(),
headerBehavior:
cdk.aws_cloudfront.CacheHeaderBehavior.allowList("Accept"),
queryStringBehavior: cdk.aws_cloudfront.CacheQueryStringBehavior.none(),
enableAcceptEncodingGzip: true,
enableAcceptEncodingBrotli: true,
}
);
Accept
ヘッダーごとにキャッシュしなければ、WebPをサポートしていないクライアントがアクセスしてきた場合にもWebPで返してしまいます。
また、用意したキャッシュポリシーを適応するのはJPEGやPNGのみにしています。
const addBehaviorOptions: cdk.aws_cloudfront.AddBehaviorOptions = {
allowedMethods: cdk.aws_cloudfront.AllowedMethods.ALLOW_GET_HEAD,
cachedMethods: cdk.aws_cloudfront.CachedMethods.CACHE_GET_HEAD,
cachePolicy: acceptCachePolicy,
originRequestPolicy:
cdk.aws_cloudfront.OriginRequestPolicy.ALL_VIEWER_EXCEPT_HOST_HEADER,
viewerProtocolPolicy:
cdk.aws_cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
responseHeadersPolicy:
cdk.aws_cloudfront.ResponseHeadersPolicy.SECURITY_HEADERS,
functionAssociations: rewriteToWebpCF2
? [
{
function: rewriteToWebpCF2,
eventType: cdk.aws_cloudfront.FunctionEventType.VIEWER_REQUEST,
},
]
: undefined,
edgeLambdas: rewriteToWebpLambdaEdge
? [
{
functionVersion: rewriteToWebpLambdaEdge.currentVersion,
eventType: cdk.aws_cloudfront.LambdaEdgeEventType.ORIGIN_REQUEST,
},
]
: undefined,
};
distribution.addBehavior(
"/*.jpe?g",
cdk.aws_cloudfront_origins.S3BucketOrigin.withOriginAccessControl(
props.websiteBucketConstruct.bucket
),
addBehaviorOptions
);
distribution.addBehavior(
"/*.png",
cdk.aws_cloudfront_origins.S3BucketOrigin.withOriginAccessControl(
props.websiteBucketConstruct.bucket
),
addBehaviorOptions
);
注意点として、Accept
ヘッダーはブラウザやアクセスの仕方によって大きく異なるため、キャッシュヒット率が低下します。
例えば、私のChromeの場合はimage/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8
やtext/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
で、Safariはimage/webp,image/avif,image/jxl,image/heic,image/heic-sequence,video/*;q=0.8,image/png,image/svg+xml,image/*;q=0.8,*/*;q=0.5
です。
もし、キャッシュヒット率を下げたくないのであれば、Viewer RequestでCloudFront Functionsを動かし、Accept
ヘッダー内にimage/webp
を含んでいる場合はx-available-webp
などとカスタムヘッダーを付与した上で、URIの末尾に.webp
を付与してリクエストするといった対応が必要になります。
CloudFront Functionsで判定する場合
CloudFront Functionsで判定する場合について紹介します。
行っていることは以下のとおりです。
Accept
ヘッダー内にimage/webp
を含む かつ 拡張子がjpeg or jpg or pngの場合、URIの末尾に.webp
を付与してリクエストするAccept
ヘッダー内にimage/webp
を含まない かつ.webp
にアクセスしようとしている場合は末尾の.webp
を削除してリクエストする
async function handler(event) {
const request = event.request;
const uri = request.uri;
// Check WebP support in Accept header
const headers = request.headers;
const acceptHeader = headers.accept ? headers.accept.value : '';
const supportsWebP = acceptHeader.includes('image/webp');
// Regular expression for image extensions
const imageExtRegex = /\.(jpe?g|png)$/i;
if (supportsWebP && imageExtRegex.test(uri)) {
// Add custom header to track WebP conversion attempt
request.headers['x-original-uri'] = {
value: uri
};
// Append .webp to the URI for supported browsers
request.uri = `${uri}.webp`;
}
// Handle .webp requests for non-supporting browsers
else if (uri.endsWith('.webp') && !supportsWebP) {
// Remove .webp extension to get original image
request.uri = uri.slice(0, -5);
}
return request;
}
実際に挙動を確認してみましょう。
まずはAccept
ヘッダーにimage/webp
を指定せずにnon__97.png
にリクエストをした場合です。
> curl -I https://www.non-97.net/non__97.png
HTTP/2 200
content-type: image/png
content-length: 76942
date: Fri, 24 Jan 2025 05:22:37 GMT
last-modified: Fri, 24 Jan 2025 03:43:32 GMT
etag: "cac2eacf135495f8eb947890b6c84526"
x-amz-server-side-encryption: AES256
accept-ranges: bytes
server: AmazonS3
x-cache: Miss from cloudfront
via: 1.1 23bc6d6a912d17773e1bf97197cbfc1e.cloudfront.net (CloudFront)
x-amz-cf-pop: NRT12-P1
alt-svc: h3=":443"; ma=86400
x-amz-cf-id: ddWdCadlo-Lj8rrv32SNK6u6RPoUgnRTbN9MAjHm9rptyna6JVyunA==
x-xss-protection: 1; mode=block
x-frame-options: SAMEORIGIN
referrer-policy: strict-origin-when-cross-origin
x-content-type-options: nosniff
strict-transport-security: max-age=31536000
> curl -I https://www.non-97.net/non__97.png
HTTP/2 200
content-type: image/png
content-length: 76942
date: Fri, 24 Jan 2025 05:22:37 GMT
last-modified: Fri, 24 Jan 2025 03:43:32 GMT
etag: "cac2eacf135495f8eb947890b6c84526"
x-amz-server-side-encryption: AES256
accept-ranges: bytes
server: AmazonS3
x-cache: Hit from cloudfront
via: 1.1 9b8a6e30994167e8de984036681d4ff6.cloudfront.net (CloudFront)
x-amz-cf-pop: NRT12-P1
alt-svc: h3=":443"; ma=86400
x-amz-cf-id: UsFAHVde0acvEnQVSK9yEi_7aj5nUHEJBR_cIoPeAh3bUU2gAckBbg==
age: 4
x-xss-protection: 1; mode=block
x-frame-options: SAMEORIGIN
referrer-policy: strict-origin-when-cross-origin
x-content-type-options: nosniff
strict-transport-security: max-age=31536000
ステータスコードが200でcontent-type
がimage/png
と、正常にPNGファイルにアクセスできていることが分かります。また、2回目のアクセスではキャッシュヒットしていることが分かります。
続いて、Accept
ヘッダーにimage/webp
を指定してnon__97.png
にリクエストをした場合です。
> curl -I https://www.non-97.net/non__97.png -H "Accept:image/webp"
HTTP/2 200
content-type: image/webp
content-length: 5464
date: Fri, 24 Jan 2025 05:23:18 GMT
last-modified: Fri, 24 Jan 2025 03:43:34 GMT
etag: "54b0857ccfbab67746434fb9042aacff"
x-amz-server-side-encryption: AES256
accept-ranges: bytes
server: AmazonS3
x-cache: Miss from cloudfront
via: 1.1 d8a0cc77a7428fd572abace71c0eeda2.cloudfront.net (CloudFront)
x-amz-cf-pop: NRT12-P1
alt-svc: h3=":443"; ma=86400
x-amz-cf-id: DDhAyxXtrdajsW6tzfUWybww3leTKQ0ohF6QBvvQt1ZMwuj1-H6APA==
x-xss-protection: 1; mode=block
x-frame-options: SAMEORIGIN
referrer-policy: strict-origin-when-cross-origin
x-content-type-options: nosniff
strict-transport-security: max-age=31536000
> curl -I https://www.non-97.net/non__97.png -H "Accept:image/webp"
HTTP/2 200
content-type: image/webp
content-length: 5464
date: Fri, 24 Jan 2025 05:23:18 GMT
last-modified: Fri, 24 Jan 2025 03:43:34 GMT
etag: "54b0857ccfbab67746434fb9042aacff"
x-amz-server-side-encryption: AES256
accept-ranges: bytes
server: AmazonS3
x-cache: Hit from cloudfront
via: 1.1 269160a4d1e0a4937fee2132fea7cb32.cloudfront.net (CloudFront)
x-amz-cf-pop: NRT12-P1
alt-svc: h3=":443"; ma=86400
x-amz-cf-id: Ftx5QMM7YcnwcY3ULkEf314YhfhvgJFS-nEpiFSRYfyUThoyCRVRRg==
age: 3
x-xss-protection: 1; mode=block
x-frame-options: SAMEORIGIN
referrer-policy: strict-origin-when-cross-origin
x-content-type-options: nosniff
strict-transport-security: max-age=31536000
ステータスコードが200でcontent-type
がimage/webp
と、正常にWebPファイルにアクセスできていることが分かります。また、2回目のアクセスではキャッシュヒットしていることが分かります。
続いて、続いて、Accept
ヘッダーにimage/webp
を指定してnon__97_2.png
にリクエストをした場合です。
> curl -I https://www.non-97.net/non__97_2.png -H "Accept:image/webp"
HTTP/2 404
content-type: text/html
content-length: 12
date: Fri, 24 Jan 2025 06:56:18 GMT
last-modified: Fri, 24 Jan 2025 03:43:32 GMT
etag: "347dfa37997b9353b3da6992f8753439"
x-amz-server-side-encryption: AES256
accept-ranges: bytes
server: AmazonS3
x-cache: Error from cloudfront
via: 1.1 b4fcd16c2d55faa87f8fa28379c19ab0.cloudfront.net (CloudFront)
x-amz-cf-pop: NRT12-P1
alt-svc: h3=":443"; ma=86400
x-amz-cf-id: qgYXjKNuYUtHe8dTr9CwZoWNHl_wp17iOZSPoTFZpIchYo9f9783Dw==
x-xss-protection: 1; mode=block
x-frame-options: SAMEORIGIN
referrer-policy: strict-origin-when-cross-origin
x-content-type-options: nosniff
strict-transport-security: max-age=31536000
はい、404エラーになりました。なぜならnon__97_2.png.webp
というオブジェクトがS3バケット上に存在しないためです。
CloudFront Functionsの最大実行時間は1msです。
そのため、オブジェクトにHEADでアクセスして、実際に存在するのか判定する時間的余裕はありません。
ということで、CloudFront FunctionsでWebPの判定をする場合は、JPEGやPNGに対応してWebPファイルが必ず存在している必要があります。
Lambda@Edgeで判定する場合
Lambda@Edge版も紹介します。
Lambda@Edgeでは以下のような処理を行っています。
Accept
ヘッダー内にimage/webp
を含む かつ 拡張子がjpeg or jpg or pngの場合- URIの末尾に
.webp
を付与した上で、HeadObjectを行い、S3バケット上に.webp
のオブジェクトが存在するか判定する- 存在する場合はオリジンにリクエストする
- 存在しない場合は元のURIでリクエストする
- URIの末尾に
Lambda@EdgeはOrigin Requestの場合タイムアウトは最大30秒です。
そのため、オリジンに実際にリクエストを投げる前にHEADをして、オブジェクトが存在するかどうかの判定を行うことが可能です。
また、Lambda@EdgeはOrigin Requestでも動作させることが可能なので、キャッシュが効いている場合は実行されないのも嬉しいポイントです。
実際のコードは以下のとおりです。
import { CloudFrontRequestEvent } from "aws-lambda";
import {
S3Client,
HeadObjectCommand,
NotFound,
S3ServiceException,
} from "@aws-sdk/client-s3";
// Constants
const IMAGE_EXTENSION_PATTERN = /\.(jpe?g|png)$/i;
const s3Client = new S3Client({
followRegionRedirects: true,
region: process.env.AWS_REGION,
});
/**
* Lambda@Edge handler for WebP image conversion
*/
export const handler = async (event: CloudFrontRequestEvent) => {
const request = event.Records[0].cf.request;
const uri = request.uri;
// Check WebP support using case-insensitive header check
const acceptHeader = request.headers["accept"]?.[0]?.value ?? "";
const supportsWebP = acceptHeader.toLowerCase().includes("image/webp");
// Process if the request is for an image and browser supports WebP
if (supportsWebP && IMAGE_EXTENSION_PATTERN.test(uri)) {
// Extract bucket information from origin
const s3Origin = request.origin?.s3;
if (!s3Origin?.domainName) {
return request;
}
const bucketName = s3Origin.domainName.split(".")[0];
const webpKey = uri.startsWith("/")
? uri.slice(1) + ".webp"
: uri + ".webp";
try {
// Check if WebP version exists
await s3Client.send(
new HeadObjectCommand({
Bucket: bucketName,
Key: webpKey,
})
);
// WebP exists, modify request
request.headers["x-original-uri"] = [
{
key: "x-original-uri",
value: uri,
},
];
request.uri = `${uri}.webp`;
} catch (error) {
if (error instanceof NotFound) {
// WebP file doesn't exist, silently use original image
return request;
}
// Log other errors
console.error("Error checking WebP existence:", {
region: process.env.AWS_REGION,
bucket: bucketName,
key: webpKey,
error: error as S3ServiceException,
});
}
}
return request;
};
挙動の確認をしましょう。
まずはAccept
ヘッダーにimage/webp
を指定せずにnon__97.png
にリクエストをした場合です。
> time curl -I https://www.non-97.net/non__97.png
HTTP/2 200
content-type: image/png
content-length: 76942
date: Sat, 25 Jan 2025 08:21:01 GMT
last-modified: Fri, 24 Jan 2025 03:43:32 GMT
etag: "cac2eacf135495f8eb947890b6c84526"
x-amz-server-side-encryption: AES256
accept-ranges: bytes
server: AmazonS3
x-cache: Miss from cloudfront
via: 1.1 c93d19226fbabc21cc93d39b7324186e.cloudfront.net (CloudFront)
x-amz-cf-pop: NRT12-P1
alt-svc: h3=":443"; ma=86400
x-amz-cf-id: 5SIWIxBxhnkV40hSgOjm4rOrZdQFJ3M0X6338iWYnCK-bVHRf0CG4g==
x-xss-protection: 1; mode=block
x-frame-options: SAMEORIGIN
referrer-policy: strict-origin-when-cross-origin
x-content-type-options: nosniff
strict-transport-security: max-age=31536000
________________________________________________________
Executed in 978.93 millis fish external
usr time 30.12 millis 0.20 millis 29.92 millis
sys time 24.50 millis 1.19 millis 23.30 millis
> time curl -I https://www.non-97.net/non__97.png
HTTP/2 200
content-type: image/png
content-length: 76942
date: Sat, 25 Jan 2025 08:21:01 GMT
last-modified: Fri, 24 Jan 2025 03:43:32 GMT
etag: "cac2eacf135495f8eb947890b6c84526"
x-amz-server-side-encryption: AES256
accept-ranges: bytes
server: AmazonS3
x-cache: Hit from cloudfront
via: 1.1 02ed41441a9b54c4370b988e0a610cbe.cloudfront.net (CloudFront)
x-amz-cf-pop: NRT12-P1
alt-svc: h3=":443"; ma=86400
x-amz-cf-id: D7ex8-5IhrilV70_cXh9KfZLxEQrYAUV71S5dEatHrQCxOWBp-DEEw==
age: 2
x-xss-protection: 1; mode=block
x-frame-options: SAMEORIGIN
referrer-policy: strict-origin-when-cross-origin
x-content-type-options: nosniff
strict-transport-security: max-age=31536000
________________________________________________________
Executed in 126.59 millis fish external
usr time 22.00 millis 0.19 millis 21.81 millis
sys time 20.21 millis 1.05 millis 19.15 millis
ステータスコードが200でcontent-type
がimage/png
と、正常にPNGファイルにアクセスできていることが分かります。また、2回目のアクセスではキャッシュヒットしていることが分かります。
続いて、Accept
ヘッダーにimage/webp
を指定してnon__97.png
にリクエストをした場合です。
> time curl -I https://www.non-97.net/non__97.png -H "Accept:image/webp"
HTTP/2 200
content-type: image/webp
content-length: 5464
date: Sat, 25 Jan 2025 08:21:48 GMT
last-modified: Fri, 24 Jan 2025 03:43:34 GMT
etag: "54b0857ccfbab67746434fb9042aacff"
x-amz-server-side-encryption: AES256
accept-ranges: bytes
server: AmazonS3
x-cache: Miss from cloudfront
via: 1.1 663c57b4ec4e2561ada30794913fe298.cloudfront.net (CloudFront)
x-amz-cf-pop: NRT12-P1
alt-svc: h3=":443"; ma=86400
x-amz-cf-id: jRik7ftpOjwwKHXayXRz5LGIAtTTiHDdbtSg59LmEGYoweyDuroJUw==
x-xss-protection: 1; mode=block
x-frame-options: SAMEORIGIN
referrer-policy: strict-origin-when-cross-origin
x-content-type-options: nosniff
strict-transport-security: max-age=31536000
________________________________________________________
Executed in 1.41 secs fish external
usr time 23.60 millis 0.23 millis 23.37 millis
sys time 20.66 millis 1.32 millis 19.34 millis
> time curl -I https://www.non-97.net/non__97.png -H "Accept:image/webp"
HTTP/2 200
content-type: image/webp
content-length: 5464
date: Sat, 25 Jan 2025 08:21:48 GMT
last-modified: Fri, 24 Jan 2025 03:43:34 GMT
etag: "54b0857ccfbab67746434fb9042aacff"
x-amz-server-side-encryption: AES256
accept-ranges: bytes
server: AmazonS3
x-cache: Hit from cloudfront
via: 1.1 134c3a4e049a857255b4bb45a60c4fde.cloudfront.net (CloudFront)
x-amz-cf-pop: NRT12-P1
alt-svc: h3=":443"; ma=86400
x-amz-cf-id: CMBT5KcFGOEz-ZATcLR7iN1WuW74V4CoTQ_qMrmA-MaUZ-du7Gn-Ow==
age: 7
x-xss-protection: 1; mode=block
x-frame-options: SAMEORIGIN
referrer-policy: strict-origin-when-cross-origin
x-content-type-options: nosniff
strict-transport-security: max-age=31536000
________________________________________________________
Executed in 125.54 millis fish external
usr time 22.00 millis 0.19 millis 21.81 millis
sys time 21.32 millis 1.32 millis 20.00 millis
ステータスコードが200でcontent-type
がimage/webp
と、正常にWebPファイルにアクセスできていることが分かります。また、2回目のアクセスではキャッシュヒットしていることが分かります。
続いて、続いて、Accept
ヘッダーにimage/webp
を指定してnon__97_2.png
にリクエストをした場合です。
> time curl -I https://www.non-97.net/non__97_2.png -H "Accept:image/webp"
HTTP/2 200
content-type: image/png
content-length: 128244
date: Sat, 25 Jan 2025 08:22:25 GMT
last-modified: Fri, 24 Jan 2025 03:43:32 GMT
etag: "5b0828f49a563829dce7f1eac62ae5b5"
x-amz-server-side-encryption: AES256
accept-ranges: bytes
server: AmazonS3
x-cache: Miss from cloudfront
via: 1.1 03e670dad9bf75ede7f4618a9edd6fde.cloudfront.net (CloudFront)
x-amz-cf-pop: NRT12-P1
alt-svc: h3=":443"; ma=86400
x-amz-cf-id: a5joRlz8gNvP0OWYwlrioRwm50favXBTY6iIQe2dQsckgXQITZwsiQ==
x-xss-protection: 1; mode=block
x-frame-options: SAMEORIGIN
referrer-policy: strict-origin-when-cross-origin
x-content-type-options: nosniff
strict-transport-security: max-age=31536000
________________________________________________________
Executed in 1.68 secs fish external
usr time 31.37 millis 0.21 millis 31.15 millis
sys time 24.37 millis 1.10 millis 23.27 millis
> time curl -I https://www.non-97.net/non__97_2.png -H "Accept:image/webp"
HTTP/2 200
content-type: image/png
content-length: 128244
date: Sat, 25 Jan 2025 08:22:25 GMT
last-modified: Fri, 24 Jan 2025 03:43:32 GMT
etag: "5b0828f49a563829dce7f1eac62ae5b5"
x-amz-server-side-encryption: AES256
accept-ranges: bytes
server: AmazonS3
x-cache: Hit from cloudfront
via: 1.1 8d25f33c84d5316b7281d56e4e9f0340.cloudfront.net (CloudFront)
x-amz-cf-pop: NRT12-P1
alt-svc: h3=":443"; ma=86400
x-amz-cf-id: lFfSyjgHdF1cc0gurzCRU3aXSx2XrPMdPrbsV1cg8uauefrTI5G4JQ==
age: 3
x-xss-protection: 1; mode=block
x-frame-options: SAMEORIGIN
referrer-policy: strict-origin-when-cross-origin
x-content-type-options: nosniff
strict-transport-security: max-age=31536000
________________________________________________________
Executed in 128.18 millis fish external
usr time 23.77 millis 191.00 micros 23.58 millis
sys time 19.20 millis 952.00 micros 18.25 millis
Accept:image/webp
とヘッダーを指定しましたが、non__97_2.png.webp
というオブジェクトは存在しないため、non__97_2.png
にアクセスしcontent-type: image/png
が返ってきました。2回目のアクセス数ではキャッシュが効いていることも確認できます。
おまけで、Accept
ヘッダーにimage/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8
を指定してnon__97_2.png
やdir/non__97_dir.png
にリクエストをした場合です。
> time curl -I https://www.non-97.net/non__97_2.png -H "image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8"
HTTP/2 200
content-type: image/png
content-length: 128244
date: Sat, 25 Jan 2025 08:23:26 GMT
last-modified: Fri, 24 Jan 2025 03:43:32 GMT
etag: "5b0828f49a563829dce7f1eac62ae5b5"
x-amz-server-side-encryption: AES256
accept-ranges: bytes
server: AmazonS3
x-cache: Miss from cloudfront
via: 1.1 8d25f33c84d5316b7281d56e4e9f0340.cloudfront.net (CloudFront)
x-amz-cf-pop: NRT12-P1
alt-svc: h3=":443"; ma=86400
x-amz-cf-id: gG5y1JuR-cYwRI727Gxc4oJowkshuy855_pTUjbU8u_CsQOqKK-T2w==
x-xss-protection: 1; mode=block
x-frame-options: SAMEORIGIN
referrer-policy: strict-origin-when-cross-origin
x-content-type-options: nosniff
strict-transport-security: max-age=31536000
________________________________________________________
Executed in 734.39 millis fish external
usr time 17.09 millis 0.22 millis 16.87 millis
sys time 18.93 millis 1.26 millis 17.67 millis
> time curl -I https://www.non-97.net/non__97_2.png -H "image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8"
HTTP/2 200
content-type: image/png
content-length: 128244
date: Sat, 25 Jan 2025 08:23:26 GMT
last-modified: Fri, 24 Jan 2025 03:43:32 GMT
etag: "5b0828f49a563829dce7f1eac62ae5b5"
x-amz-server-side-encryption: AES256
accept-ranges: bytes
server: AmazonS3
x-cache: Hit from cloudfront
via: 1.1 134c3a4e049a857255b4bb45a60c4fde.cloudfront.net (CloudFront)
x-amz-cf-pop: NRT12-P1
alt-svc: h3=":443"; ma=86400
x-amz-cf-id: glCJvAz5ms1Je8ReSqxQITHGsQLw3Iw51Bqkmte6C84NlTiP5ihDJQ==
age: 11
x-xss-protection: 1; mode=block
x-frame-options: SAMEORIGIN
referrer-policy: strict-origin-when-cross-origin
x-content-type-options: nosniff
strict-transport-security: max-age=31536000
________________________________________________________
Executed in 176.21 millis fish external
usr time 22.99 millis 204.00 micros 22.79 millis
sys time 22.70 millis 980.00 micros 21.72 millis
> time curl -I https://www.non-97.net/dir/non__97_dir.png -H "Accept:image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8"
HTTP/2 200
content-type: image/webp
content-length: 5464
date: Sat, 25 Jan 2025 10:53:22 GMT
last-modified: Fri, 24 Jan 2025 03:43:34 GMT
etag: "54b0857ccfbab67746434fb9042aacff"
x-amz-server-side-encryption: AES256
accept-ranges: bytes
server: AmazonS3
x-cache: Miss from cloudfront
via: 1.1 663c57b4ec4e2561ada30794913fe298.cloudfront.net (CloudFront)
x-amz-cf-pop: NRT12-P1
alt-svc: h3=":443"; ma=86400
x-amz-cf-id: fkiupgaBuplOJgz0CynJEput_cf4dm-g3ZjS8DE5vMSQz87ACIW0Yw==
x-xss-protection: 1; mode=block
x-frame-options: SAMEORIGIN
referrer-policy: strict-origin-when-cross-origin
x-content-type-options: nosniff
strict-transport-security: max-age=31536000
________________________________________________________
Executed in 1.52 secs fish external
usr time 19.51 millis 0.22 millis 19.29 millis
sys time 20.23 millis 1.22 millis 19.01 millis
> time curl -I https://www.non-97.net/dir/non__97_dir.png -H "Accept:image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8"
HTTP/2 200
content-type: image/webp
content-length: 5464
date: Sat, 25 Jan 2025 10:53:22 GMT
last-modified: Fri, 24 Jan 2025 03:43:34 GMT
etag: "54b0857ccfbab67746434fb9042aacff"
x-amz-server-side-encryption: AES256
accept-ranges: bytes
server: AmazonS3
x-cache: Hit from cloudfront
via: 1.1 ad7e3284c55a83a5ec392e0b7c426e94.cloudfront.net (CloudFront)
x-amz-cf-pop: NRT12-P1
alt-svc: h3=":443"; ma=86400
x-amz-cf-id: 4kr0cSIppMaAQCdM53zFLyC4KxXsc65Ybqwc1JJgdkO7TFTcLOw6bg==
age: 2
x-xss-protection: 1; mode=block
x-frame-options: SAMEORIGIN
referrer-policy: strict-origin-when-cross-origin
x-content-type-options: nosniff
strict-transport-security: max-age=31536000
________________________________________________________
Executed in 123.69 millis fish external
usr time 20.41 millis 0.20 millis 20.21 millis
sys time 19.55 millis 1.03 millis 18.52 millis
はい、問題なく動作しています。
CloudFrontとS3でWebサイトを構成している場合、Acceptヘッダーに応じて動的にWebPを配信するのはひと工夫が必要
CloudFrontとS3を使ったWebサイトでWebPの画像を配信してみました。
CloudFrontとS3でWebサイトを構成している場合、Acceptヘッダーに応じて動的にWebPを配信するのはひと工夫が必要です。
個人的にはLambda@Edgeで対応するのが良いのかなと考えています。
この記事が誰かの助けになれば幸いです。
以上、クラウド事業本部 コンサルティング部の のんピ(@non____97)でした!